In [3]:
from openai import OpenAI
import os, json
from dotenv import load_dotenv
from datetime import datetime
from PIL import Image
from io import BytesIO
import google.generativeai as genai # Nano Banana
from IPython.display import display
# =======================
# Configuración API
# =======================
load_dotenv()
openai_client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
genai.configure(api_key=os.getenv("GOOGLE_API_KEY")) # Nano Banana
# (PARCHE MÍNIMO) Modelo de imágenes Gemini
model_img = genai.GenerativeModel("gemini-2.5-flash-image-preview")
# =======================
# Variables globales para control de tokens
# =======================
acumulado_tokens = 0
acumulado_usd = 0.0
# =======================
# Helper JSON
# =======================
def safe_json_parse(raw_text: str):
cleaned = raw_text.strip()
if cleaned.startswith("```"):
cleaned = cleaned.strip("`")
cleaned = cleaned.replace("json\n", "").replace("json", "", 1).strip()
try:
return json.loads(cleaned)
except:
return {}
# =======================
# >>> Helpers de VALIDACIÓN (agregados)
# =======================
def pedir_texto_no_vacio(mensaje: str) -> str:
while True:
val = input(mensaje).strip()
if val:
return val
print("⚠️ Ingresá un texto no vacío.")
def pedir_opcion_entera(mensaje: str, opciones_validas: range) -> int:
while True:
val = input(mensaje).strip()
try:
n = int(val)
if n in opciones_validas:
return n
print(f"⚠️ Opción inválida. Elegí un número en {opciones_validas.start}–{opciones_validas.stop-1}.")
except ValueError:
print("⚠️ Ingresá un número entero válido.")
def pedir_entero_min(mensaje: str, minimo: int = 1) -> int:
while True:
val = input(mensaje).strip()
try:
n = int(val)
if n >= minimo:
return n
print(f"⚠️ Ingresá un entero mayor o igual a {minimo}.")
except ValueError:
print("⚠️ Ingresá un número entero válido.")
def pedir_fecha_ddmmyyyy(mensaje: str) -> str:
# Devuelve string en formato DD/MM/YYYY si es válido
while True:
s = input(mensaje).strip()
try:
datetime.strptime(s, "%d/%m/%Y")
return s
except ValueError:
print("⚠️ Fecha inválida. Formato esperado: DD/MM/YYYY (ej: 05/09/2025).")
def pedir_hora_hhmm(mensaje: str) -> str:
# Devuelve HHMM (4 dígitos) validando 00:00–23:59
while True:
s = input(mensaje).strip()
if len(s) == 4 and s.isdigit():
hh = int(s[:2]); mm = int(s[2:])
if 0 <= hh <= 23 and 0 <= mm <= 59:
return s
print("⚠️ Hora inválida. Ingresá 4 dígitos HHMM (ej: 0830, 1300).")
# =======================
# Helper TOKENS
# =======================
def calcular_costo(response, nombre="Prompt", archivo="costos_totales.txt"):
global acumulado_tokens, acumulado_usd
if not hasattr(response, "usage"):
return
in_tokens = response.usage.prompt_tokens
out_tokens = response.usage.completion_tokens
total_tokens = response.usage.total_tokens
in_price = in_tokens * 0.00000015
out_price = out_tokens * 0.0000006
costo_total = in_price + out_price
acumulado_tokens += total_tokens
acumulado_usd += costo_total
with open(archivo, "a", encoding="utf-8") as f:
f.write(
f"{nombre} → Tokens usados: {total_tokens} "
f"(entrada={in_tokens}, salida={out_tokens}), "
f"USD {costo_total:.6f}\n"
)
# 👉 Nuevo: cálculo para imágenes
def calcular_costo_imagen(nombre="Imagen", archivo="costos_totales.txt", costo_usd=0.04):
global acumulado_usd
acumulado_usd += costo_usd
with open(archivo, "a", encoding="utf-8") as f:
f.write(f"{nombre} → Costo fijo por imagen: USD {costo_usd:.6f}\n")
def guardar_resumen_tokens(archivo="costos_totales.txt"):
with open(archivo, "a", encoding="utf-8") as f:
f.write("\n=== RESUMEN TOTAL ===\n")
f.write(f"Tokens totales consumidos: {acumulado_tokens}\n")
f.write(f"Costo total estimado: USD {acumulado_usd:.6f}\n")
# =======================
# Recolección de datos
# =======================
print("👋 Bienvenido al Organizador de Escapadas IA")
print("Por favor completá los siguientes datos usando SOLO números:\n")
# >>> Validación: texto no vacío
destino = pedir_texto_no_vacio("Destino del viaje (texto libre): ")
# Transporte
print("\nSeleccioná el medio de transporte:")
transportes = {
1: "auto",
2: "micro",
3: "avión",
4: "tren"
}
for k, v in transportes.items():
print(f"{k}. {v.capitalize()}")
# >>> Validación: opción 1–4
transporte_op = pedir_opcion_entera("Seleccione Medio de Transporte: ", range(1, 5))
transporte = transportes.get(transporte_op, "auto")
# Personas
# >>> Validación: entero >= 1
cant_personas = pedir_entero_min("\nCantidad de personas: ", minimo=1)
# Fechas y horarios (con reintento si el rango es inválido)
while True:
# >>> Validación: fecha
fecha_inicio = pedir_fecha_ddmmyyyy("\nFecha de inicio (ej: 05/09/2025): ")
# >>> Validación: hora HHMM
hora_llegada = pedir_hora_hhmm("Hora de llegada (HHMM, ej: 1300 para las 13:00): ")
# Mantiene lógica original de conversión
hora_llegada = f"{hora_llegada[:2]}:{hora_llegada[2:]}" # convierte a HH:MM
fecha_regreso = pedir_fecha_ddmmyyyy("\nFecha de regreso (ej: 07/09/2025): ")
hora_regreso = pedir_hora_hhmm("Hora de regreso (HHMM, ej: 0830 para las 8:30): ")
hora_regreso = f"{hora_regreso[:2]}:{hora_regreso[2:]}"
try:
dt_inicio = datetime.strptime(f"{fecha_inicio} {hora_llegada}", "%d/%m/%Y %H:%M")
dt_regreso = datetime.strptime(f"{fecha_regreso} {hora_regreso}", "%d/%m/%Y %H:%M")
if dt_regreso <= dt_inicio:
print("⚠️ La fecha/hora de regreso debe ser posterior a la de inicio. Intentá nuevamente.")
continue
break
except ValueError:
# Por si algo raro escapa a los validadores
print("⚠️ Fecha u hora inválida. Intentá nuevamente.")
cant_dias = (dt_regreso.date() - dt_inicio.date()).days + 1
if cant_dias < 1:
# Con las validaciones anteriores no debería ocurrir, pero mantenemos tu chequeo original
raise ValueError("⚠️ La fecha de regreso debe ser posterior a la fecha de inicio.")
# Presupuesto
print("\nSeleccioná el nivel de presupuesto:")
presupuestos = {
1: "bajo",
2: "medio",
3: "medio-alto",
4: "alto"
}
print("1. Bajo → Opciones económicas, transporte público, hostels.")
print("2. Medio → Balance entre costo y comodidad.")
print("3. Medio-alto → Hoteles 3-4⭐, experiencias destacadas.")
print("4. Alto → Lujo, experiencias premium.")
# >>> Validación: opción 1–4
presupuesto_op = pedir_opcion_entera("Seleccione el Nivel de Presupuesto: ", range(1, 5))
presupuesto = presupuestos.get(presupuesto_op, "medio")
# Modos de viaje
print("\nSeleccioná el modo de viaje:")
modos = {
1: ("Exprímelo", "Aprovechar al máximo cada hora."),
2: ("Relax", "Ritmo tranquilo, descansos largos."),
3: ("Cultural", "Museos, historia, arquitectura."),
4: ("Gastronómico", "Comidas y vinos locales."),
5: ("Aventura", "Deportes y excursiones."),
6: ("Familiar", "Opciones aptas para todas las edades.")
}
for k, v in modos.items():
print(f"{k}. {v[0]} → {v[1]}")
# >>> Validación: opción 1–6
modo_op = pedir_opcion_entera("Seleccione el Modo de Viaje: ", range(1, 7))
modo_viaje = modos.get(modo_op, modos[1])[0]
# Niños si es Familiar
ninos_menores_12 = False
if modo_viaje == "Familiar":
print("\n¿Hay niños menores de 12 años en el grupo?")
print("1. Sí")
print("2. No")
# >>> Validación: opción 1–2
resp = pedir_opcion_entera("¿Hay niños menores de 12 años en el grupo?: ", range(1, 3))
ninos_menores_12 = True if resp == 1 else False
# Temporada manual
print("\n¿En qué temporada vas a viajar?")
print("1. Alta (vacaciones, feriados largos, temporada turística)")
print("2. Baja (resto del año)")
# >>> Validación: opción 1–2
temp_op = pedir_opcion_entera("¿En qué temporada vas a viajar?: ", range(1, 3))
temporada = "alta" if temp_op == 1 else "baja"
# =======================
# Resumen inicial
# =======================
print("\n=== Resumen de tu viaje ===")
print(f"Destino: {destino}")
print(f"Medio de transporte: {transporte}")
print(f"Duración: {cant_dias} días, para {cant_personas} personas.")
print(f"Llegada: {fecha_inicio} a las {hora_llegada}")
print(f"Regreso: {fecha_regreso} a las {hora_regreso}")
print(f"Presupuesto estimado: {presupuesto}")
print(f"Modo de viaje seleccionado: {modo_viaje}")
if modo_viaje == "Familiar":
print(f"¿Viajan niños menores de 12 años?: {'Sí' if ninos_menores_12 else 'No'}")
print(f"Temporada: {temporada.upper()}")
# =======================
# Prompt A - Intake JSON
# =======================
intake_prompt = f"""
Sos un organizador de viajes.
Devolvé SOLO un JSON válido.
JSON esperado:
{{
"param": {{
"dest": "{destino}",
"transporte": "{transporte}",
"dias": {cant_dias},
"pers": {cant_personas},
"presupuesto": "{presupuesto}",
"modo": "{modo_viaje}",
"fecha_inicio": "{fecha_inicio}",
"hora_llegada": "{hora_llegada}",
"fecha_regreso": "{fecha_regreso}",
"hora_regreso": "{hora_regreso}",
"ninos_menores_12": {str(ninos_menores_12).lower()},
"temporada": "{temporada}"
}}
}}
"""
intake_response = openai_client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "Respondé SOLO con JSON válido y breve."},
{"role": "user", "content": intake_prompt}
],
temperature=0.2
)
calcular_costo(intake_response, "Prompt A - Intake")
raw_intake = intake_response.choices[0].message.content
intake_json = safe_json_parse(raw_intake)
print("\n=== Intake JSON ===")
print(json.dumps(intake_json, indent=2, ensure_ascii=False))
# =======================
# Prompt A - Itinerario
# =======================
itinerario_prompt = f"""
Usá este JSON:
{json.dumps(intake_json, indent=2, ensure_ascii=False)}
Generá un itinerario detallado de {cant_dias} días.
Formato:
Día N - Zona
09:00-11:00 Actividad
[Si corresponde: (traslado: XX min medio)]
11:15-13:00 Actividad
[Si corresponde: (traslado: XX min medio)]
13:15-14:30 Almuerzo (2 opciones)
[Si corresponde: (traslado: XX min medio)]
15:00-17:00 Actividad
[Si corresponde: (traslado: XX min medio)]
17:15-19:00 Actividad
20:00 Cena (2 opciones)
Reglas adicionales:
- Mostrá tiempos de traslado SOLO cuando cambie el lugar de la actividad.
- No repitas traslado si la siguiente actividad ocurre en el mismo sitio.
- Si el modo de viaje es "Familiar" y "ninos_menores_12" es true:
- Incluí actividades adaptadas y marcá con 👶.
- Si alguna actividad no es apta para menores o es muy exigente, marcala con ⚠️ y proponé una "Actividad alternativa" en ese mismo día y horario.
- Si el modo de viaje es "Familiar" pero "ninos_menores_12" es false:
- NO incluyas actividades adaptadas para niños.
- Considerá la temporada:
- Si es ALTA → recomendá reservas anticipadas, horarios tempranos y opciones alternativas por alta demanda.
- Si es BAJA → advertí sobre posibles cierres o menor disponibilidad de actividades.
- Al final de cada día, incluir un breve resumen con tips.
"""
itinerario_response = openai_client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "Devolvé solo itinerario en texto limpio."},
{"role": "user", "content": itinerario_prompt}
],
temperature=0.4
)
calcular_costo(itinerario_response, "Prompt A - Itinerario")
content = itinerario_response.choices[0].message.content
print("\n=== Itinerario generado ===")
print(content)
with open("itinerario_final.txt", "w", encoding="utf-8") as f:
f.write(content)
# =======================
# Prompt QA - Control de calidad (agrupado y visual mejorado)
# =======================
qa_prompt = f"""
Revisá el siguiente itinerario y generá un JSON con advertencias relevantes agrupadas por día.
Itinerario:
{content}
Devolvé SOLO un JSON con este formato:
{{
"alertas": {{
"Día 2": [
"Actividad X puede no ser apta para niños",
"Traslado mayor a 60 minutos"
],
"Día 5": [
"Actividad Y puede ser excesiva"
]
}}
}}
Reglas:
- Agrupá todas las advertencias bajo el día correspondiente.
- Marcá traslados mayores a 60 minutos.
- Detectá jornadas con exceso de actividades según el modo elegido.
- Señalá actividades no aptas para niños si hay menores de 12 años.
- Si no hay niños, no incluyas esas advertencias.
- Si la temporada es "alta" → incluir advertencia de "Reserva anticipada".
- Si la temporada es "baja" → incluir advertencia de "Atracciones cerradas por estacionalidad".
- No incluyas advertencias de temporada que no correspondan.
- Si no hay alertas para un día, no incluyas ese día en el JSON.
"""
qa_response = openai_client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "Actuá como auditor de itinerarios. Respondé SOLO con JSON válido."},
{"role": "user", "content": qa_prompt}
],
temperature=0.2
)
calcular_costo(qa_response, "Prompt QA - Auditoría")
qa_json = safe_json_parse(qa_response.choices[0].message.content)
print("\n=== QA del itinerario ===")
for dia, alertas in qa_json.get("alertas", {}).items():
print(f"\n📅 {dia}")
print("-" * (len(dia) + 4))
for alerta in alertas:
icono = "⚠️"
if "niños" in alerta.lower():
icono = "👶"
elif "traslado" in alerta.lower():
icono = "🕒"
elif "presupuesto" in alerta.lower():
icono = "💸"
elif "reserva" in alerta.lower() or "estacionalidad" in alerta.lower():
icono = "📅"
print(f"{icono} {alerta}")
print("-" * 40)
# =======================
# Extracción de lugares y servicios
# =======================
lugares = []
palabras_clave = [
"hotel", "restaurante", "bodega", "actividad", "excursión",
"remis", "taxi", "auto de alquiler", "transfer", "museo",
"café", "bar", "parque", "mercado", "zoológico", "plaza"
]
for line in content.splitlines():
texto = line.strip()
if not texto:
continue
if any(palabra in texto.lower() for palabra in palabras_clave):
lugares.append(texto)
elif any(palabra.istitle() for palabra in texto.split()):
lugares.append(texto)
print("\n=== Lugares y servicios detectados en el itinerario ===")
print(lugares)
with open("lugares.json", "w", encoding="utf-8") as f:
json.dump(lugares, f, indent=2, ensure_ascii=False)
# =======================
# Extraer puntos clave del itinerario
# =======================
puntos = []
for line in content.splitlines(): # (PARCHE) usar 'content'
if any(keyword in line.lower() for keyword in ["actividad", "almuerzo", "cena", "tour", "visita", "excursión", "paseo"]):
puntos.append(line.strip())
lista_puntos = " | ".join(puntos)
# =======================
# (PARCHE) Subtítulo del flyer
# =======================
intereses = modo_viaje # reutiliza la selección del usuario
# =======================
# Prompts de Imagen (mapa dinámico + flyer)
# =======================
prompts_img = {
"Mapa": f"""
Mapa turístico ilustrado estilo vintage de {destino} basado en el itinerario, con textura de papel antiguo y paleta cálida (beige, terracota, verdes suaves).
Base: contorno simplificado de {destino} o del área de viaje, con líneas curvas que conectan varios círculos grandes.
Dentro de cada círculo, mostrar **pequeñas escenas realistas del destino** (sin texto), seleccionadas a partir del itinerario y estos puntos clave:
{lista_puntos}
Reglas para elegir las 4 escenas (adaptadas a {destino}):
- Si {destino} es de montaña/lago → usar: cordillera/cumbres, lago/valle, ciclista o senderismo local, bosque/parque típico.
- Si es de costa → usar: playa/olas, puerto/muelle, embarcación local, acantilado o dunas.
- Si es urbano → usar: arquitectura icónica/plaza central, mercado o café típico, parque urbano, actividad cultural.
- Si es zona vitivinícola o gastronómica → incluir viñedos con racimos, copa/bodega (realista), plato regional.
- Si hay nieve → incluir escena invernal (centro de ski o paisaje nevado).
- Priorizar escenas mencionadas en el itinerario; si alguna falta, sustituir por el hito natural/cultural más representativo de {destino}.
- Estilo híbrido: mapa e iconografía mínima ilustrada + círculos con escenas **realistas** (pintura/foto-look).
- **No agregar texto ni etiquetas en ningún lugar**.
Formato: horizontal 16:9, PNG, composición limpia y nítida, coherente y equilibrada.
""",
"Flyer": f"""
Flyer turístico 16:9 para {destino}, estilo travel-poster moderno.
Título: 'Escapada a {destino}'.
Subtítulo: {intereses}.
Imagen icónica del lugar, composición limpia, margen seguro para texto.
Devuelve únicamente la imagen en PNG.
"""
}
# =======================
# Función para generar imágenes
# =======================
def generar_imagenes_lite(prompts: dict, prefix="prompt5lite"):
for nombre, p in prompts.items():
try:
print(f"\n=== Generando {nombre} ===")
# (PARCHE) llamada al modelo Gemini
response_img = model_img.generate_content(p)
saved = False
# Mismo esquema de extracción usado anteriormente
if hasattr(response_img, "candidates") and response_img.candidates:
parts = response_img.candidates[0].content.parts
for part in parts:
if hasattr(part, "inline_data") and part.inline_data:
img = Image.open(BytesIO(part.inline_data.data))
img.save(f"{prefix}_{nombre}.png")
display(img) # Muestra la imagen en la celda del notebook
print(f"✅ {nombre} generado → {prefix}_{nombre}.png")
calcular_costo_imagen(f"Imagen {nombre}")
saved = True
break
if not saved:
print(f"⚠️ No se generó imagen para {nombre}. Gemini no devolvió inline_data")
except Exception as e:
print(f"❌ Error generando {nombre}: {e}")
# =======================
# Prompt C - Simulación de datos de contacto
# =======================
prompt_contactos = f"""
A partir de la siguiente lista de lugares y servicios detectados en el itinerario:
{json.dumps(lugares, indent=2, ensure_ascii=False)}
Simulá que buscás en la web sus datos de contacto.
Devolvé SOLO un JSON con este formato:
[
{{
"nombre": "Nombre del lugar o empresa",
"tipo": "hotel | restaurante | bodega | actividad | excursión | transporte",
"web": "URL ficticia o realista",
"telefono": "Teléfono ficticio con código de país",
"email": "Email ficticio con formato válido"
}}
]
Reglas:
- Si aparecen hoteles, restaurantes, bodegas, actividades o excursiones → devolvé contactos de esos lugares.
- Si en el itinerario figuran medios de transporte local como autos de alquiler, remises, taxis o transfers → incluí también empresas proveedoras de ese servicio en la zona del destino.
- Los datos deben ser plausibles y consistentes con Argentina.
"""
contactos_response = openai_client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "Respondé SOLO con JSON válido."},
{"role": "user", "content": prompt_contactos}
],
temperature=0.4
)
calcular_costo(contactos_response, "Prompt C - Contactos")
raw_contactos = contactos_response.choices[0].message.content
contactos_json = safe_json_parse(raw_contactos)
with open("contactos.json", "w", encoding="utf-8") as f:
json.dump(contactos_json, f, indent=2, ensure_ascii=False)
print("\n=== Lugares y servicios con datos de contacto simulados ===")
for lugar in contactos_json:
print(f"📍 {lugar['nombre']} ({lugar['tipo']})")
print(f" 🌐 Web: {lugar['web']}")
print(f" 📞 Tel: {lugar['telefono']}")
print(f" ✉️ Email: {lugar['email']}")
print("-" * 50)
# =======================
# Generar imágenes
# =======================
generar_imagenes_lite(prompts_img, prefix="prompt5lite")
# =======================
# Guardar resumen total de tokens
# =======================
guardar_resumen_tokens()
👋 Bienvenido al Organizador de Escapadas IA
Por favor completá los siguientes datos usando SOLO números:
Seleccioná el medio de transporte:
1. Auto
2. Micro
3. Avión
4. Tren
Seleccioná el nivel de presupuesto:
1. Bajo → Opciones económicas, transporte público, hostels.
2. Medio → Balance entre costo y comodidad.
3. Medio-alto → Hoteles 3-4⭐, experiencias destacadas.
4. Alto → Lujo, experiencias premium.
Seleccioná el modo de viaje:
1. Exprímelo → Aprovechar al máximo cada hora.
2. Relax → Ritmo tranquilo, descansos largos.
3. Cultural → Museos, historia, arquitectura.
4. Gastronómico → Comidas y vinos locales.
5. Aventura → Deportes y excursiones.
6. Familiar → Opciones aptas para todas las edades.
¿Hay niños menores de 12 años en el grupo?
1. Sí
2. No
¿En qué temporada vas a viajar?
1. Alta (vacaciones, feriados largos, temporada turística)
2. Baja (resto del año)
=== Resumen de tu viaje ===
Destino: Punta Tombo
Medio de transporte: avión
Duración: 7 días, para 4 personas.
Llegada: 15/09/2025 a las 08:00
Regreso: 21/09/2025 a las 22:00
Presupuesto estimado: alto
Modo de viaje seleccionado: Familiar
¿Viajan niños menores de 12 años?: Sí
Temporada: ALTA
=== Intake JSON ===
{
"param": {
"dest": "Punta Tombo",
"transporte": "avión",
"dias": 7,
"pers": 4,
"presupuesto": "alto",
"modo": "Familiar",
"fecha_inicio": "15/09/2025",
"hora_llegada": "08:00",
"fecha_regreso": "21/09/2025",
"hora_regreso": "22:00",
"ninos_menores_12": true,
"temporada": "alta"
}
}
=== Itinerario generado ===
**Itinerario de 7 días en Punta Tombo**
**Día 1 - Punta Tombo**
08:00-09:00 Llegada y traslado al hotel
09:00-11:00 Instalación en el hotel y descanso
11:15-13:00 Visita a la Reserva Natural Punta Tombo 👶
13:15-14:30 Almuerzo
- Opción 1: Restaurante "El Faro"
- Opción 2: Café "La Playa"
15:00-17:00 Observación de pingüinos en la colonia 👶
17:15-19:00 Paseo por la costa
20:00 Cena
- Opción 1: "Parrilla del Mar"
- Opción 2: "La Casa del Pescador"
*Resumen del día: Asegúrate de llevar protector solar y agua. Reserva para las cenas por alta demanda.*
---
**Día 2 - Punta Tombo**
09:00-11:00 Excursión guiada por la colonia de pingüinos 👶
11:15-13:00 Taller de manualidades sobre la fauna local 👶
13:15-14:30 Almuerzo
- Opción 1: "Comedor de la Reserva"
- Opción 2: "Bistró del Pingüino"
15:00-17:00 Visita al Centro de Interpretación
17:15-19:00 Caminata por senderos naturales
20:00 Cena
- Opción 1: "Restaurante Patagonia"
- Opción 2: "Café del Parque"
*Resumen del día: Lleva ropa cómoda para las caminatas. Considera hacer reservas para el taller de manualidades.*
---
**Día 3 - Punta Tombo**
09:00-11:00 Excursión en kayak por la costa ⚠️
*Actividad alternativa: Paseo en bote por la bahía*
11:15-13:00 Visita a la playa y juegos en la arena 👶
13:15-14:30 Almuerzo
- Opción 1: "La Cabaña del Mar"
- Opción 2: "Snack Playa"
15:00-17:00 Observación de aves en el área costera 👶
17:15-19:00 Taller de fotografía de naturaleza
20:00 Cena
- Opción 1: "El Rincón del Mar"
- Opción 2: "Sabor Patagónico"
*Resumen del día: Mantén a los niños hidratados y protegidos del sol. Verifica disponibilidad para el taller de fotografía.*
---
**Día 4 - Punta Tombo**
09:00-11:00 Excursión a la Isla de los Pájaros ⚠️
*Actividad alternativa: Visita a un mirador cercano*
11:15-13:00 Picnic en la playa 👶
13:15-14:30 Almuerzo
- Opción 1: "Restaurante del Parque"
- Opción 2: "Cafetería de la Isla"
15:00-17:00 Taller de conservación de la fauna marina 👶
17:15-19:00 Visita a un criadero de tortugas
20:00 Cena
- Opción 1: "El Asador"
- Opción 2: "Restaurante Mar y Tierra"
*Resumen del día: Lleva comida para el picnic. Asegúrate de reservar para el taller de conservación.*
---
**Día 5 - Punta Tombo**
09:00-11:00 Excursión a la Gruta de las Manos ⚠️
*Actividad alternativa: Visita a un museo local*
11:15-13:00 Actividad de arte en la playa 👶
13:15-14:30 Almuerzo
- Opción 1: "Café Cultural"
- Opción 2: "Comedor de Artesanos"
15:00-17:00 Visita a la Reserva de Fauna Silvestre 👶
17:15-19:00 Taller de cocina patagónica
20:00 Cena
- Opción 1: "La Parrilla de Punta"
- Opción 2: "Cocina Tradicional"
*Resumen del día: Asegúrate de llevar ropa adecuada para las actividades al aire libre. Considera reservar para el taller de cocina.*
---
**Día 6 - Punta Tombo**
09:00-11:00 Excursión en bicicleta por la costa 👶
11:15-13:00 Visita a un parque de aventuras ⚠️
*Actividad alternativa: Paseo por senderos fáciles*
13:15-14:30 Almuerzo
- Opción 1: "Parrilla del Parque"
- Opción 2: "Café de la Costa"
15:00-17:00 Actividades recreativas en la playa 👶
17:15-19:00 Observación de fauna marina
20:00 Cena
- Opción 1: "El Rincón del Sabor"
- Opción 2: "Mariscos del Sur"
*Resumen del día: Mantén a los niños seguros durante las actividades. Reserva para la cena debido a la alta demanda.*
---
**Día 7 - Punta Tombo**
09:00-11:00 Visita a la Feria de Artesanías 👶
11:15-13:00 Actividad de cierre: Reflexiones sobre la naturaleza 👶
13:15-14:30 Almuerzo
- Opción 1: "Café de la Feria"
- Opción 2: "Comedor de la Reserva"
15:00-17:00 Últimas compras de recuerdos
17:15-19:00 Regreso al hotel y preparación para el viaje
20:00 Cena de despedida
- Opción 1: "Restaurante El Último Refugio"
- Opción 2: "La Mesa de Punta"
*Resumen del día: Disfruta de las últimas horas en Punta Tombo. Asegúrate de realizar las compras antes de la cena.*
=== QA del itinerario ===
📅 Día 3
---------
👶 Actividad en kayak puede no ser apta para niños
🕒 Traslado mayor a 60 minutos
----------------------------------------
📅 Día 4
---------
👶 Actividad a la Isla de los Pájaros puede no ser apta para niños
🕒 Traslado mayor a 60 minutos
----------------------------------------
📅 Día 5
---------
👶 Actividad a la Gruta de las Manos puede no ser apta para niños
🕒 Traslado mayor a 60 minutos
----------------------------------------
📅 Día 6
---------
👶 Actividad en parque de aventuras puede no ser apta para niños
🕒 Traslado mayor a 60 minutos
----------------------------------------
=== Lugares y servicios detectados en el itinerario ===
['**Itinerario de 7 días en Punta Tombo**', '**Día 1 - Punta Tombo**', '08:00-09:00 Llegada y traslado al hotel', '09:00-11:00 Instalación en el hotel y descanso', '11:15-13:00 Visita a la Reserva Natural Punta Tombo 👶', '13:15-14:30 Almuerzo', '- Opción 1: Restaurante "El Faro"', '- Opción 2: Café "La Playa"', '15:00-17:00 Observación de pingüinos en la colonia 👶', '17:15-19:00 Paseo por la costa', '20:00 Cena', '- Opción 1: "Parrilla del Mar"', '- Opción 2: "La Casa del Pescador"', '*Resumen del día: Asegúrate de llevar protector solar y agua. Reserva para las cenas por alta demanda.*', '**Día 2 - Punta Tombo**', '09:00-11:00 Excursión guiada por la colonia de pingüinos 👶', '11:15-13:00 Taller de manualidades sobre la fauna local 👶', '13:15-14:30 Almuerzo', '- Opción 1: "Comedor de la Reserva"', '- Opción 2: "Bistró del Pingüino"', '15:00-17:00 Visita al Centro de Interpretación', '17:15-19:00 Caminata por senderos naturales', '20:00 Cena', '- Opción 1: "Restaurante Patagonia"', '- Opción 2: "Café del Parque"', '*Resumen del día: Lleva ropa cómoda para las caminatas. Considera hacer reservas para el taller de manualidades.*', '**Día 3 - Punta Tombo**', '09:00-11:00 Excursión en kayak por la costa ⚠️', '*Actividad alternativa: Paseo en bote por la bahía*', '11:15-13:00 Visita a la playa y juegos en la arena 👶', '13:15-14:30 Almuerzo', '- Opción 1: "La Cabaña del Mar"', '- Opción 2: "Snack Playa"', '15:00-17:00 Observación de aves en el área costera 👶', '17:15-19:00 Taller de fotografía de naturaleza', '20:00 Cena', '- Opción 1: "El Rincón del Mar"', '- Opción 2: "Sabor Patagónico"', '*Resumen del día: Mantén a los niños hidratados y protegidos del sol. Verifica disponibilidad para el taller de fotografía.*', '**Día 4 - Punta Tombo**', '09:00-11:00 Excursión a la Isla de los Pájaros ⚠️', '*Actividad alternativa: Visita a un mirador cercano*', '11:15-13:00 Picnic en la playa 👶', '13:15-14:30 Almuerzo', '- Opción 1: "Restaurante del Parque"', '- Opción 2: "Cafetería de la Isla"', '15:00-17:00 Taller de conservación de la fauna marina 👶', '17:15-19:00 Visita a un criadero de tortugas', '20:00 Cena', '- Opción 1: "El Asador"', '- Opción 2: "Restaurante Mar y Tierra"', '*Resumen del día: Lleva comida para el picnic. Asegúrate de reservar para el taller de conservación.*', '**Día 5 - Punta Tombo**', '09:00-11:00 Excursión a la Gruta de las Manos ⚠️', '*Actividad alternativa: Visita a un museo local*', '11:15-13:00 Actividad de arte en la playa 👶', '13:15-14:30 Almuerzo', '- Opción 1: "Café Cultural"', '- Opción 2: "Comedor de Artesanos"', '15:00-17:00 Visita a la Reserva de Fauna Silvestre 👶', '17:15-19:00 Taller de cocina patagónica', '20:00 Cena', '- Opción 1: "La Parrilla de Punta"', '- Opción 2: "Cocina Tradicional"', '*Resumen del día: Asegúrate de llevar ropa adecuada para las actividades al aire libre. Considera reservar para el taller de cocina.*', '**Día 6 - Punta Tombo**', '09:00-11:00 Excursión en bicicleta por la costa 👶', '11:15-13:00 Visita a un parque de aventuras ⚠️', '*Actividad alternativa: Paseo por senderos fáciles*', '13:15-14:30 Almuerzo', '- Opción 1: "Parrilla del Parque"', '- Opción 2: "Café de la Costa"', '15:00-17:00 Actividades recreativas en la playa 👶', '17:15-19:00 Observación de fauna marina', '20:00 Cena', '- Opción 1: "El Rincón del Sabor"', '- Opción 2: "Mariscos del Sur"', '*Resumen del día: Mantén a los niños seguros durante las actividades. Reserva para la cena debido a la alta demanda.*', '**Día 7 - Punta Tombo**', '09:00-11:00 Visita a la Feria de Artesanías 👶', '11:15-13:00 Actividad de cierre: Reflexiones sobre la naturaleza 👶', '13:15-14:30 Almuerzo', '- Opción 1: "Café de la Feria"', '- Opción 2: "Comedor de la Reserva"', '15:00-17:00 Últimas compras de recuerdos', '17:15-19:00 Regreso al hotel y preparación para el viaje', '20:00 Cena de despedida', '- Opción 1: "Restaurante El Último Refugio"', '- Opción 2: "La Mesa de Punta"', '*Resumen del día: Disfruta de las últimas horas en Punta Tombo. Asegúrate de realizar las compras antes de la cena.*']
=== Lugares y servicios con datos de contacto simulados ===
📍 Hotel Punta Tombo (hotel)
🌐 Web: http://www.hotelpuntatombo.com
📞 Tel: +54 280 123 4567
✉️ Email: info@hotelpuntatombo.com
--------------------------------------------------
📍 Restaurante El Faro (restaurante)
🌐 Web: http://www.restauranteelfaro.com
📞 Tel: +54 280 234 5678
✉️ Email: reservas@restauranteelfaro.com
--------------------------------------------------
📍 Café La Playa (restaurante)
🌐 Web: http://www.cafelaplaya.com
📞 Tel: +54 280 345 6789
✉️ Email: contacto@cafelaplaya.com
--------------------------------------------------
📍 Parrilla del Mar (restaurante)
🌐 Web: http://www.parrilladelmar.com
📞 Tel: +54 280 456 7890
✉️ Email: info@parrilladelmar.com
--------------------------------------------------
📍 La Casa del Pescador (restaurante)
🌐 Web: http://www.lacasadelpescador.com
📞 Tel: +54 280 567 8901
✉️ Email: reservas@lacasadelpescador.com
--------------------------------------------------
📍 Comedor de la Reserva (restaurante)
🌐 Web: http://www.comedordelareserva.com
📞 Tel: +54 280 678 9012
✉️ Email: info@comedordelareserva.com
--------------------------------------------------
📍 Bistró del Pingüino (restaurante)
🌐 Web: http://www.bistrodopingüino.com
📞 Tel: +54 280 789 0123
✉️ Email: contacto@bistrodopingüino.com
--------------------------------------------------
📍 Restaurante Patagonia (restaurante)
🌐 Web: http://www.restaurantepatagonia.com
📞 Tel: +54 280 890 1234
✉️ Email: info@restaurantepatagonia.com
--------------------------------------------------
📍 Café del Parque (restaurante)
🌐 Web: http://www.cafedelparque.com
📞 Tel: +54 280 901 2345
✉️ Email: reservas@cafedelparque.com
--------------------------------------------------
📍 La Cabaña del Mar (restaurante)
🌐 Web: http://www.lacabanadelmar.com
📞 Tel: +54 280 012 3456
✉️ Email: info@lacabanadelmar.com
--------------------------------------------------
📍 Snack Playa (restaurante)
🌐 Web: http://www.snackplaya.com
📞 Tel: +54 280 123 4568
✉️ Email: contacto@snackplaya.com
--------------------------------------------------
📍 El Rincón del Mar (restaurante)
🌐 Web: http://www.elrincondelmar.com
📞 Tel: +54 280 234 5679
✉️ Email: reservas@elrincondelmar.com
--------------------------------------------------
📍 Sabor Patagónico (restaurante)
🌐 Web: http://www.saborpatagonico.com
📞 Tel: +54 280 345 6780
✉️ Email: info@saborpatagonico.com
--------------------------------------------------
📍 El Asador (restaurante)
🌐 Web: http://www.elasador.com
📞 Tel: +54 280 456 7891
✉️ Email: reservas@elasador.com
--------------------------------------------------
📍 Restaurante Mar y Tierra (restaurante)
🌐 Web: http://www.restaurantemarytierra.com
📞 Tel: +54 280 567 8902
✉️ Email: info@restaurantemarytierra.com
--------------------------------------------------
📍 La Parrilla de Punta (restaurante)
🌐 Web: http://www.laparrilladepunta.com
📞 Tel: +54 280 678 9013
✉️ Email: contacto@laparrilladepunta.com
--------------------------------------------------
📍 Cocina Tradicional (restaurante)
🌐 Web: http://www.cocinatradicional.com
📞 Tel: +54 280 789 0124
✉️ Email: info@cocinatradicional.com
--------------------------------------------------
📍 Parrilla del Parque (restaurante)
🌐 Web: http://www.parrilladelparque.com
📞 Tel: +54 280 890 1235
✉️ Email: reservas@parrilladelparque.com
--------------------------------------------------
📍 Café de la Costa (restaurante)
🌐 Web: http://www.cafedelacosta.com
📞 Tel: +54 280 901 2346
✉️ Email: info@cafedelacosta.com
--------------------------------------------------
📍 El Rincón del Sabor (restaurante)
🌐 Web: http://www.elrincondelsabor.com
📞 Tel: +54 280 012 3457
✉️ Email: contacto@elrincondelsabor.com
--------------------------------------------------
📍 Mariscos del Sur (restaurante)
🌐 Web: http://www.mariscosdelsur.com
📞 Tel: +54 280 123 4569
✉️ Email: reservas@mariscosdelsur.com
--------------------------------------------------
📍 Restaurante El Último Refugio (restaurante)
🌐 Web: http://www.restauranteelultimorefugio.com
📞 Tel: +54 280 234 5670
✉️ Email: info@restauranteelultimorefugio.com
--------------------------------------------------
📍 La Mesa de Punta (restaurante)
🌐 Web: http://www.lamesadepunta.com
📞 Tel: +54 280 345 6781
✉️ Email: contacto@lamesadepunta.com
--------------------------------------------------
=== Generando Mapa ===
✅ Mapa generado → prompt5lite_Mapa.png === Generando Flyer ===
✅ Flyer generado → prompt5lite_Flyer.png